import type { DeepReadonly, FunctionalComponent, Ref } from 'vue'
import { computed, readonly, ref } from 'vue'
import { connect } from '@formily/vue'
import type { PageParams, PageResponse } from '@/utils/request'
import usePaginationQuery from '@/hooks/usePaginationQuery'
import type { QueryFormField } from '@/components/QueryForm/types'
import QueryForm from '@/components/QueryForm'
import type { PaginationProps, TableColumn } from '@/components/ColumnsTable'
import ColumnsTable from '@/components/ColumnsTable'

interface QueryParams<T> {
  getList: <T, P extends PageParams>(params: P & Record<string, any>) => Promise<PageResponse<T>> | (<T>() => Promise<PageResponse<T>>)
  initValues?: Record<string, any> & PageParams & Partial<T>
}

interface IReturn<T, Key> {
  queryParams: Ref<Record<string, any> & PageParams>
  handleSearch: () => Promise<void>
  Table: FunctionalComponent<{ rowKey?: (row: T) => Key; disabled?: boolean }>
  data: DeepReadonly<Ref<T[]>>
}

function usePageTable<T, Key = string | number>(
  query: QueryParams<T>,
  columns: TableColumn<T>[]
): IReturn<T, Key>
function usePageTable<T, Key = string | number>(
  query: QueryParams<T> & { fields: QueryFormField<string & keyof T>[] },
  columns: TableColumn<T>[]
): IReturn<T, Key> & { QueryForm: FunctionalComponent }
function usePageTable<T, Key = string | number>(
  query: QueryParams<T> & { fields: QueryFormField<string & keyof T>[] },
  columns: TableColumn<T>[],
  selection: 'single'
): IReturn<T, Key> & { QueryForm: FunctionalComponent } & { selections: Ref<T | undefined> }
function usePageTable<T, Key = string | number>(
  query: QueryParams<T> & { fields: QueryFormField<string & keyof T>[] },
  columns: TableColumn<T>[],
  selection: 'multiple'
): IReturn<T, Key> & { QueryForm: FunctionalComponent } & { selections: Ref<T[]> }
function usePageTable<T, Key = string | number>(
  query: QueryParams<T>,
  columns: TableColumn<T>[],
  selection: 'single'
): IReturn<T, Key> & { QueryForm: FunctionalComponent } & { selections: Ref<T | undefined> }
function usePageTable<T, Key = string | number>(
  query: QueryParams<T>,
  columns: TableColumn<T>[],
  selection: 'multiple'
): IReturn<T, Key> & { selections: Ref<T[]> }
function usePageTable<T, Key = string | number>(
  query: QueryParams<T> & { fields?: QueryFormField<string & keyof T>[] },
  columns: TableColumn<T>[],
  selection: 'single' | 'multiple' | undefined = undefined,
): any {
  const { getList } = query
  const initParams = () => ({ pageNum: 1, pageSize: 10, ...query.initValues })
  const queryParams = ref<Record<string, any> & PageParams>(initParams())

  const { handleQuery, loading, data, total } = usePaginationQuery<T>(getList)
  const handleSearch = async () => handleQuery(queryParams.value)
  const queryForm = query.fields && (() => (
    <QueryForm
      form={queryParams.value}
      fields={query.fields!}
      onSearch={() => {
        queryParams.value.page = 1
        void handleSearch()
      }}
      onRest={() => {
        queryParams.value = initParams()
        queryParams.value.page = 1
        void handleSearch()
      }}
      onUpdate:form={(value: any) => queryParams.value = value}
    />
  ))

  const pagination = computed<PaginationProps>(() => ({
    total: total.value,
    page: queryParams.value.pageNum,
    limit: queryParams.value.pageSize,
    onPagination: async ({ page, limit }) => {
      queryParams.value.pageNum = page
      queryParams.value.pageSize = limit
      void handleSearch()
    },
  }))

  const selections = selection ? ref<Key[]>([]) as Ref<Key[]> : ref<Key>() as Ref<Key>

  const Table: FunctionalComponent<{ rowKey?: (row: T) => Key; disabled?: boolean }> = (props) => {
    return (
      <ColumnsTable<T, Key>
        columns={columns}
        rowKey={props.rowKey}
        loading={loading.value}
        pagination={pagination.value}
        data={data.value}
        selection={selection}
        onChange={value => selections.value = value}
        selections={selections.value}
        disabled={props.disabled}
      />
    )
  }

  const readonlyData = readonly(data)
  return {
    queryParams,
    handleSearch,
    selections,
    QueryForm: queryForm!,
    Table,
    data: readonlyData,
  }
}

export default usePageTable

export function useTableSelect<T, Key>(
  query: QueryParams<T> & { fields?: QueryFormField<string & keyof T>[] },
  columns: TableColumn<T>[],
  selection: 'single' | 'multiple' = 'single',
  rowKey: (row: T) => Key,
) {
  const { getList } = query
  const initParams = () => ({ pageNum: 1, pageSize: 10, ...query.initValues })
  const queryParams = ref<Record<string, any> & PageParams>(initParams())

  const { handleQuery, loading, data, total } = usePaginationQuery<T>(getList)
  const handleSearch = async () => handleQuery(queryParams.value)
  const queryForm = query.fields && (() => (
    <QueryForm
      form={queryParams.value}
      fields={query.fields!}
      onSearch={() => {
        queryParams.value.page = 1
        void handleSearch()
      }}
      onRest={() => {
        queryParams.value = initParams()
        queryParams.value.page = 1
        void handleSearch()
      }}
      onUpdate:form={(value: any) => queryParams.value = value}
    />
  ))

  const pagination = computed<PaginationProps>(() => ({
    total: total.value,
    page: queryParams.value.pageNum,
    limit: queryParams.value.pageSize,
    onPagination: async ({ page, limit }) => {
      queryParams.value.pageNum = page
      queryParams.value.pageSize = limit
      void handleSearch()
    },
  }))
  type ValueType<T extends 'multiple' | 'single' | undefined> = T extends 'multiple' ? Key[] : (
    T extends 'single' ? Key : undefined
  )
  const Table: FunctionalComponent<{
    rowKey?: (row: T) => Key
    disabled?: boolean
    value?: ValueType<typeof selection>
    onChange: (value: ValueType<typeof selection>) => void
  }>
    = props => (
      <ColumnsTable<T, Key>
        columns={columns}
        rowKey={props.rowKey}
        loading={loading.value}
        pagination={pagination.value}
        data={data.value}
        selection={selection}
        onChange={props.onChange}
        selections={props.value}
        disabled={props.disabled}
      />
    )

  void handleSearch()

  const TableSelect: FunctionalComponent<{
    disabled?: boolean
    value: ValueType<typeof selection>
    onChange: (value: ValueType<typeof selection>) => void
  }>
    = props => (
      <div class="flex flex-col overflow-auto w-full">
        {queryForm && <queryForm />}
        <Table rowKey={rowKey} disabled={props.disabled} onChange={props.onChange} value={props.value} />
      </div>
    )
  const readonlyData = readonly(data)
  return { TableSelect: connect(TableSelect), data: readonlyData }
}
